use crate::entry::Entry;
use hostname::get;

use std::convert::From;
use std::convert::Into;
use std::env;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::BufWriter;
use std::io::Result;
use std::io::Write;
use std::panic;
use std::process;
use std::sync::RwLock;
use std::sync::{Arc, Mutex};
use std::thread;
use std::{fmt, io};

#[derive(Clone, Copy)]
pub enum LevelFormat {
    Num,    //数字level
    Letter, // 文本level
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
// 定义日志级别
#[derive(Clone)]
pub enum Level {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
    Crit,
    Fatal,
    Panic,
}

impl From<u8> for Level {
    fn from(value: u8) -> Self {
        match value {
            0 => Level::Trace,
            1 => Level::Debug,
            2 => Level::Info,
            3 => Level::Warn,
            4 => Level::Error,
            5 => Level::Crit,
            6 => Level::Fatal,
            7 => Level::Panic,
            _ => panic!("Unknown log level"),
        }
    }
}

pub fn parser_log_level_from_u8(level: u8) -> String {
    match level {
        0 => "Trace".to_string(),
        1 => "Debug".to_string(),
        2 => "Info".to_string(),
        3 => "Warn".to_string(),
        4 => "Error".to_string(),
        5 => "Crit".to_string(),
        6 => "Fatal".to_string(),
        7 => "Panic".to_string(),
        _ => panic!("Unknown log level"),
    }
}
impl fmt::Display for Level {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Level::Trace => write!(f, "Trace"),
            Level::Debug => write!(f, "Debug"),
            Level::Info => write!(f, "Info"),
            Level::Warn => write!(f, "Warn"),
            Level::Error => write!(f, "Error"),
            Level::Crit => write!(f, "Crit"),
            Level::Fatal => write!(f, "Fatal"),
            Level::Panic => write!(f, "Panic"),
        }
    }
}

// 定义日志格式,支持文本和json
#[allow(dead_code)]
pub enum LogFormat {
    Json,
    JsonPtrtty,
    Text,
}

#[derive(Clone, Copy)]
pub enum TimeFormat {
    RFC3339,
    RFC2822,
}

pub struct Logger {
    hostname: String,
    log_format: LogFormat,
    level_format: LevelFormat,
    time_format: TimeFormat,
    level: Level,
    term_enable: bool,
    term_color_enable: bool,
    file_output: Option<FileOutput>,
    std_handler: RwLock<io::Stdout>,
    pid: u32,
    program_name: String,
    trace_enable: bool,
}

impl Default for Logger {
    fn default() -> Self {
        let program_name = env::args().next().unwrap();
        let pid = process::id();

        Logger {
            hostname: get_hostname(),
            log_format: LogFormat::Text,
            level_format: LevelFormat::Letter,
            time_format: TimeFormat::RFC3339,
            level: Level::Trace,
            term_enable: true,
            term_color_enable: true,
            file_output: None,
            std_handler: RwLock::new(io::stdout()),
            pid,
            program_name,
            trace_enable: true,
        }
    }
}

impl Logger {
    pub fn set_file_output(&mut self, file_path: String, sync: bool) {
        self.file_output = Some(FileOutput::new(file_path, sync).unwrap());
    }

    /// 关闭标准输出到term,默认打开, 生产运行时请关闭
    pub fn set_enable_term_output(&mut self, b: bool) {
        self.term_enable = b;
    }
    /// 关闭term 颜色渲染，默认打开,只有term才会渲染颜色,输出到文件不渲染
    pub fn set_enable_term_color(&mut self, b: bool) {
        self.term_color_enable = b;
    }

    /// 设置日志级别,日志级别为enum Logger::Level
    pub fn set_log_level(&mut self, level: Level) {
        self.level = level;
    }

    pub fn set_level_format(&mut self, level_format: LevelFormat) {
        self.level_format = level_format;
    }

    // 设置时间格式，支持rfc2822和rfc3339格式，传入emun Logger::TimeFormat
    pub fn set_time_format(&mut self, time_format: TimeFormat) {
        self.time_format = time_format;
    }

    pub fn set_enable_trace(&mut self, b: bool) {
        self.trace_enable = b
    }

    /// push数据到不同的队列
    /// 发送至stdout时直接发送,效率会比较低,实时性会比较高
    /// 发送到文件时异步发送，生产上请以该模式运行，性能最好
    fn push(&mut self, entry: Entry) {
        if self.level <= entry.level.into() {
            if self.term_enable {
                let log = match self.log_format {
                    LogFormat::Json => entry.format_json().unwrap(),
                    LogFormat::JsonPtrtty => entry.format_json_pretty().unwrap(),
                    LogFormat::Text => entry
                        .format_line_all(self.term_color_enable, self.level_format)
                        .unwrap(),
                };

                self.write_stdout(log.clone());
            }
            let log_to_file = match self.log_format {
                LogFormat::Json => entry.format_json().unwrap(),
                LogFormat::JsonPtrtty => entry.format_json_pretty().unwrap(),
                LogFormat::Text => entry.format_line_all(false, self.level_format).unwrap(),
            };
            if Level::Panic == entry.level.into() {
                self.write_file_sync(log_to_file);
            } else {
                self.write_file(log_to_file)
            }
        }
    }

    fn write_stdout(&mut self, data: Vec<u8>) {
        let mut stdout_lock = self.std_handler.write().unwrap();
        stdout_lock.write_all(&data).expect("write stdout error");
        stdout_lock.flush().expect("flush stdout error");
    }

    fn write_file(&mut self, data: Vec<u8>) {
        let _ = self.file_output.as_mut().unwrap().write_log(data);
    }

    fn write_file_sync(&mut self, data: Vec<u8>) {
        let _ = self.file_output.as_mut().unwrap().sync_write(data);
    }
}

impl Logger {
    pub fn basical_with_trace(&mut self, message: &str, level: u8, f: String, l: usize) -> Entry {
        Entry::new(
            self.hostname.clone(),
            self.program_name.clone(),
            self.pid,
            level,
            Some(f),
            Some(l),
            message.to_string(),
            self.time_format,
        )
    }
    pub fn basical_without_trace(&mut self, message: &str, level: u8) -> Entry {
        Entry::new_without_trace(
            self.hostname.clone(),
            self.program_name.clone(),
            self.pid,
            level,
            message.to_string(),
            self.time_format,
        )
    }
    pub fn trace(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Trace as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Trace as u8);
            self.push(e)
        }
    }

    pub fn debug(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Debug as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Debug as u8);
            self.push(e)
        }
    }

    pub fn info(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Info as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Info as u8);
            self.push(e)
        }
    }
    pub fn warn(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Warn as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Warn as u8);
            self.push(e)
        }
    }
    pub fn error(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Error as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Error as u8);
            self.push(e)
        }
    }
    pub fn crit(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Crit as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Crit as u8);
            self.push(e)
        }
    }
    pub fn fatal(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Fatal as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Fatal as u8);
            self.push(e)
        }
    }
    pub fn panic(&mut self, message: &str, f: String, l: usize) {
        if self.trace_enable {
            let e = self.basical_with_trace(message, Level::Panic as u8, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, Level::Panic as u8);
            self.push(e)
        }
    }

    // 数字日志,限制30个日志等级,过多的日志等级对于项目来说没有增益
    pub fn lognum(&mut self, level: u8, message: &str, f: String, l: usize) {
        if level > 30 {
            self.write_stdout("max log level is 30 \n".as_bytes().into());
            return;
        }
        if self.trace_enable {
            let e = self.basical_with_trace(message, level, f, l);
            self.push(e)
        } else {
            let e = self.basical_without_trace(message, level);
            self.push(e)
        }
    }
}

fn get_hostname() -> String {
    let name = get().unwrap().into_string();
    match name {
        Ok(name) => name,
        Err(_) => "localhost".to_string(),
    }
}

pub struct FileOutput {
    fh: Arc<Mutex<BufWriter<File>>>,
    is_sync: bool,
}

impl FileOutput {
    pub fn new(file_path: String, is_sync: bool) -> Result<FileOutput> {
        let _f = OpenOptions::new()
            .create(true)
            .append(true)
            .open(file_path)
            .unwrap();
        let buf_writer = BufWriter::new(_f);
        let fh = Arc::new(Mutex::new(buf_writer));

        if !is_sync {
            let fh_clone = Arc::clone(&fh);
            // 启动一个新线程来处理自动刷新任务
            thread::spawn(move || {
                auto_flush(fh_clone);
            });

            return Ok(FileOutput { fh, is_sync });
        }

        Ok(FileOutput { fh, is_sync })
    }
    // 写入日志的方法
    pub fn write_log(&self, log: Vec<u8>) -> Result<()> {
        let mut file = self.fh.lock().unwrap();
        if self.is_sync {
            file.write_all(&log)?;
            file.flush()?;
        } else {
            file.write_all(&log)?;
        }

        Ok(())
    }

    // 专门为panic捕捉设置的sync write，async模式panic信息会无法捕捉
    pub fn sync_write(&self, log: Vec<u8>) -> Result<()> {
        let mut file = self.fh.lock().unwrap();
        let _ = file.write_all(&log);
        file.flush()?;
        Ok(())
    }
}

// 异步下刷任务
fn auto_flush(fh: Arc<Mutex<BufWriter<File>>>) {
    loop {
        std::thread::sleep(std::time::Duration::from_millis(1000));
        if let Ok(mut file) = fh.lock() {
            if !file.buffer().is_empty() {
                file.flush().unwrap();
            }
        }
    }
}
